#ifndef MEMORY
#define MEMORY

#include <map>
#include <string>

/**
	@brief niveau d'erreur

	niveau d'erreur sur la mémoire (pour les exceptions de type Error)
*/
#define MEMORY_LEVEL_ERROR			1

/**
	@brief code d'erreur

	code d'erreur en cas de mémoire pleine
*/
#define MEMORY_NOMORESPACE			1
/**
	@brief code d'erreur

	code d'erreur en cas d'écriture dans une zone non allouée
*/
#define MEMORY_WRITEONNONALLOCATED	2
/**
	@brief code d'erreur

	code d'erreur en cas de lecture depuis une zone non allouée
*/
#define MEMORY_READONNONALLOCATED	3

#define MEMORY_MODE_LOCAL   0
#define MEMORY_MODE_GLOBAL  1

#define MEMORY_MODE         MEMORY_MODE_GLOBAL

/**
	@class Memory
	@brief classe de gestion de la mémoire

	cette classe gère la mémoire locale à un contexte d'exécution (prog. principal, fonction, ...)
*/
class Memory
{
	public:
		/**
			@brief constructeur

			initialise la taille de la mémoire et réalise l'allocation dynamique
			@param [in] maxSize la taille de la mémoire à allouer
		*/
		Memory ( const unsigned int maxSize );
		/**
			@brief destructeur

			desalloue la zone réservée lors du constructeur
		*/
		~Memory ();
		/**
			@brief allocation d'un emplacement en mémoire

			alloue l'emplacement nécessaire à une variable
			@param [in] size la taille de la zone à allouer
			@return l'adresse associée à la variable
		*/
		unsigned int allocate ( const unsigned int size );
		/**
			@brief acces mémoire en écriture

			écrit une donnée en mémoire. cela revient à l'appel C memcpy ( memoire [ address ], data, size )
			@param address l'adresse où écrire
			@param data un pointeur sur la donnée à écrire
			@param size la taille de la donnée à écrire
		*/
		void set ( const unsigned int address, const void* data, const unsigned int size );
		/**
			@brief acces mémoire en lecture

			lit une donnée en mémoire. cela revient à l'appel C memcpy ( data, memoire [ address ], size )
			@param address l'adresse où lire
			@param data un pointeur sur la donnée à lire
			@param size la taille de la donnée à lire
		*/
		void get ( const unsigned int address, void* data, const unsigned int size ) const;
#ifdef UNITTEST
		/**
			@brief tests unitaires

			méthode permettant de tester le bon fonctionnement de la classe
			ajouter -DUNITTEST aux flags de compilation pour l'utiliser
		*/
		static void unitaryTest ();
#endif
	private:
#if MEMORY_MODE == MEMORY_MODE_LOCAL
		/// taille totale de la mémoire
		unsigned int _memorySize;
		/// dernier bloc libre, pour la prochaine allocation
		unsigned int _lastFreeBlock;
		/// la zone mémoire pour les allocations
		unsigned char* _memory;
#else
        unsigned int _localSize;
        unsigned int _saveLastFreeBlock;
        static unsigned int _memorySize;
        static unsigned int _lastFreeBlock;
        static unsigned char* _memory;
#endif
		/// mapping adresse vers taille allouée
		std::map < unsigned int, unsigned int > _addressToSize;
};

#endif
